﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
using System.Drawing;
using Ionic.Zip;
using System.IO;
namespace RuneLog.Stats
{
    /// <summary>
    /// Holds information for a Runescape stat
    /// </summary>
    class Stat
    {
        /// <summary>
        /// converts this object into a string representation
        /// </summary>
        /// <param name="s">the stat object to convert</param>
        /// <returns>a string representation of this Stat object.  Returns the same value as ToString</returns>
        public static implicit operator string(Stat s)
        {
            return s.ToString();
        }
        
        public override string ToString()
        {
            return Name + " - " + Experience +"["+Level+"] " + " {" + Rank + "}";
        }
        /// <summary>
        /// The Stat's level
        /// </summary>
        public int Level
        {
            get;
            private set;
        }
        /// <summary>
        /// The Stat's Rank
        /// </summary>
        public int Rank
        {
            get;
            private set;
        }
        /// <summary>
        /// The Stat's Experience
        /// </summary>
        public long Experience
        {
            get;
            private set;
        }
        /// <summary>
        /// The Stat name
        /// </summary>
        public string Name 
        {
            get;
            private set;
        }
        /// <summary>
        /// Constructs a new Stat object
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="rank"></param>
        /// <param name="level"></param>
        /// <param name="name"></param>
        public Stat(long exp, int rank, int level, string name)
        {
            Experience = exp;
            Rank = rank;
            Level = level;
            Name = name;
        }
        /// <summary>
        /// holds a list of stats to be referenced by id
        /// </summary>
        private static Dictionary<int, String> statByID = new Dictionary<int, string>();

        static Stat()
        {
            statByID.Add(0, "Overall");
            statByID.Add(1, "Attack");
            statByID.Add(2, "Defence");
            statByID.Add(3, "Strength");
            statByID.Add(4, "Constitution");
            statByID.Add(5, "Ranged");
            statByID.Add(6, "Prayer");
            statByID.Add(7, "Magic");
            statByID.Add(8, "Cooking");
            statByID.Add(9, "Woodcutting");
            statByID.Add(10, "Fletching");
            statByID.Add(11, "Fishing");
            statByID.Add(12, "Firemaking");
            statByID.Add(13, "Crafting");
            statByID.Add(14, "Smithing");
            statByID.Add(15, "Mining");
            statByID.Add(16, "Herblore");
            statByID.Add(17, "Agility");
            statByID.Add(18, "Thieving");
            statByID.Add(19, "Slayer");
            statByID.Add(20, "Farming");
            statByID.Add(21, "Runecrafting");
            statByID.Add(22, "Hunter");
            statByID.Add(23, "Construction");
            statByID.Add(24, "Summoning");
            statByID.Add(25, "Dungeoneering");
            statByID.Add(26, "Divination");

        }
        public static int GetIDFromString(string name)
        {
            return statByID.First(x => x.Value == name).Key;
        }
        /// <summary>
        /// returns the skill name based on its numeric id
        /// </summary>
        /// <param name="id">the skill id</param>
        /// <returns>the name of the skill</returns>
        public static String GetByID(int id)
        {
            return statByID[id];
        }
    }
    /// <summary>
    /// Used to hold a list of stats for a player
    /// All stats can be referenced by name, or by numeric ID
    /// </summary>
    class UserStats
    {
        /// <summary>
        /// The player's name
        /// </summary>
        public string Name
        {
            get;
            private set;
        }
        /// <summary>
        /// Gets the skill info by numeric ID.
        /// <remarks>this is not recommended to use because of the overhead from calling reflection methods</remarks>
        /// </summary>
        /// <param name="id">the skill ID</param>
        /// <returns>a stat object representing the skill given by the id</returns>
        public Stat this[int id]
        {
            get
            {
                return (Stat)typeof(UserStats).GetProperty(Stat.GetByID(id)).GetValue(this, null);
            }
        }
        /// <summary>
        /// the player's overall stat
        /// </summary>
        public Stat Overall
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's attack stat
        /// </summary>
        public Stat Attack
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's defence stat
        /// </summary>
        public Stat Defence
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's strength stat
        /// </summary>
        public Stat Strength
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's constitution stat
        /// </summary>
        public Stat Constitution
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's ranging stat
        /// </summary>
        public Stat Ranged
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's prayer stat
        /// </summary>
        public Stat Prayer
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's magic stat
        /// </summary>
        public Stat Magic
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's cooking stat
        /// </summary>
        public Stat Cooking
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's woodcutting stat
        /// </summary>
        public Stat Woodcutting
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's fletching stat
        /// </summary>
        public Stat Fletching
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's fishing stat
        /// </summary>
        public Stat Fishing
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's firemaking stat
        /// </summary>
        public Stat Firemaking
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's crafting stat
        /// </summary>
        public Stat Crafting
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's smithing stat
        /// </summary>
        public Stat Smithing
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's mining stat
        /// </summary>
        public Stat Mining
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's herblore stat
        /// </summary>
        public Stat Herblore
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's agility stat
        /// </summary>
        public Stat Agility
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's thieving stat
        /// </summary>
        public Stat Thieving
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's slayer stat
        /// </summary>
        public Stat Slayer
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's farming stat
        /// </summary>
        public Stat Farming
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's runecrafting stat
        /// </summary>
        public Stat Runecrafting
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's hunter stat
        /// </summary>
        public Stat Hunter
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's construction stat
        /// </summary>
        public Stat Construction
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's summoning stat
        /// </summary>
        public Stat Summoning
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's dungeoneering stat
        /// </summary>
        public Stat Dungeoneering
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's divination stat
        /// </summary>
        public Stat Divination
        {
            get;
            private set;
        }
        /// <summary>
        /// the player's combat level
        /// </summary>
        public int CombatLevel
        {
            get
            {
                var cstats = new Stat[] { Attack, Strength, Defence, Ranged, Magic };
                var cmax = cstats.Max(x => x.Experience);
                return cstats.First(x => x.Experience == cmax).Level + Defence.Level + 2;
            }
        }
        /// <summary>
        /// the url to download stats from
        /// </summary>
        private static string DownloadURL
        {
            get
            {
                return "http://services.runescape.com/m=hiscore/index_lite.ws?player=";
            }
        }
        private static string DownloadURLOldschool
        {
            get
            {
                return "http://services.runescape.com/m=hiscore_oldschool/index_lite.ws?player=";
            }
        }

        public bool Oldschool
        {
            get;
            private set;
        }
        /// <summary>
        /// a single WebClient object to reuse
        /// </summary>
        private static System.Net.WebClient client = new System.Net.WebClient();
        /// <summary>
        /// Constructs a new UserStats object using the username given.
        /// It downloads the data from <code>http://services.runescape.com/m=hiscore/index_lite.ws?player=</code> and parses
        /// it into valid data.
        /// </summary>
        /// <param name="username">the player's username</param>
        /// <exception cref="UsernameIsNotMemberException">Thrown if the username given is not a member or does not exist</exception>
        public UserStats(string username, bool os = false)
        {
            try
            {
                string str = client.DownloadString((os ? DownloadURLOldschool : DownloadURL) + username);
                string[] lines = str.Split(new char[] { '\n' });
                for (int i = 0; i < (os ? 24 : 27); i++)
                {
                    string statname = Stat.GetByID(i);
                    string[] statinfo = lines[i].Split(new char[] { ',' });
                    typeof(UserStats).GetProperty(statname).SetValue(this, new Stat(long.Parse(statinfo[2]), int.Parse(statinfo[0]), int.Parse(statinfo[1]), statname), null);
                }
                Name = username;
                Oldschool = os;
            }
            catch (System.Net.WebException)
            {
                throw new UsernameIsNotMemberException(username + " is either not a member or not a valid account.");
            }
        }
    }

    public class UsernameIsNotMemberException : Exception
    {
        public UsernameIsNotMemberException() { }
        public UsernameIsNotMemberException(string message) : base(message) { }
        public UsernameIsNotMemberException(string message, Exception inner) : base(message, inner) { }
        protected UsernameIsNotMemberException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }
}
